package command;

import interfaces.CommandTypeInfo;

import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.lang.annotation.*;

import annotations.Default;
import exceptions.NotEnoughParameterException;
import exceptions.TooManyParamatersExceptions;
import exceptions.WrongCommand;
import exceptions.WrongParameterException;

public class CommandScanner {
	private ArrayList<CommandTypeInfo> commandTypeInfos;
	BufferedReader shellReader;
	private String command;
	Object target;

	public CommandScanner(ArrayList<CommandTypeInfo> commands,
			BufferedReader shellReader, Object target) {
		this.commandTypeInfos = commands;
		this.shellReader = shellReader;
		this.target = target;
	}

	public void fillInCommandDesc(CommandDescriptor commandDC) throws Exception {

		// read commands, split params, set them in commandDescriptor
		String readedCommandLine;

		try {
			readedCommandLine = shellReader.readLine();
			if (readedCommandLine == null || readedCommandLine.equals("")) {
				throw new WrongCommand(" wrong command or empty line");
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			throw e;
		}
		this.command = readedCommandLine;
		if (readedCommandLine.equals("help")
				|| readedCommandLine.equals("exit"))
			return;

		String[] splittedCommand = readedCommandLine.split(" ");
		String command = splittedCommand[0].toLowerCase();

		// check if third parameter is always a number
		if (splittedCommand.length == 4 && !splittedCommand[0].equals("gbs")) {
			try {
				@SuppressWarnings("unused")
				Integer intTry = Integer.parseInt(splittedCommand[3]);
			} catch (NumberFormatException e) {
				throw new WrongParameterException(
						"Wrong Parameter at position 3");
			}
		}

		// check that only playernames with ONLY letters are accepted
		if (splittedCommand.length == 2 && command.equals("crp")) {
			if (!splittedCommand[1].matches("[a-zA-Z]+"))
				throw new WrongParameterException(
						"Wrong Parameter at position 1");
		}

		// search command in commandtypeinfos

		for (int i = 0; i < commandTypeInfos.size(); i++) {
			if (command.equals(this.commandTypeInfos.get(i).getCommandName())) {
				commandDC.commandType = this.commandTypeInfos.get(i); // commanddescriptor
																		// =
																		// command

				// hole paramter liste wie sie sein soll
				Class<?>[] classOfExceptedParams = commandDC.commandType
						.getParamTypes();
				if (classOfExceptedParams.length < (splittedCommand.length - 1))
					throw new TooManyParamatersExceptions("Too many paramaters");
				if (classOfExceptedParams.length > (splittedCommand.length - 1)) {

					try {
						@SuppressWarnings("unchecked")
						Method method = ((Class) this.target)
								.getDeclaredMethod(commandDC.commandType
										.getFunction().getName(),
										classOfExceptedParams);

						String[] buffer = new String[splittedCommand.length + 1];
						Annotation[][] anno = method.getParameterAnnotations();

						if (anno.length >= 1) {
							boolean annotation_found = false;
							for (Annotation[] an : anno) {
								if (an.length >= 1)
									annotation_found = true;
							}
							if (annotation_found) {
								Default annotation;
								int defaultwert = 0;
								for (int z = 0; z < anno.length; z++) {
									for (Annotation a : anno[z]) {
										if (a instanceof Default) {
											//System.out.println("TREFFER");
											annotation = (Default) a;
											defaultwert = annotation.value();
										} 
									}
								}

								for (int a = 0; a < splittedCommand.length; a++) {
									buffer[a] = splittedCommand[a];
								}
								buffer[splittedCommand.length] = Integer
										.toString(defaultwert);
								splittedCommand = buffer;
							} else
								throw new NotEnoughParameterException(
										"Not enough Parameter");
						}
					} catch (Exception e) {
						// throw e;
						throw e;
					}

				}

				commandDC.params = new Object[classOfExceptedParams.length];

				// ansonsten setze commandDC paramter auf paramter der eingabe
				for (int j = 0; j < classOfExceptedParams.length; j++) {
					Class<?> clas = classOfExceptedParams[j];
					if (clas == int.class) {
						commandDC.params[j] = Integer
								.parseInt((String) splittedCommand[j + 1]);
					} /*
					 * else if (clas == Long.class) { commandDC.params[j] = Long
					 * .parseLong(splittedCommand[j + 1]); }
					 */else {
						commandDC.params[j] = (String) splittedCommand[j + 1]
								.toUpperCase();
					}
				}
				return;
			}

		}
		throw new WrongCommand("wrong command");
	}

	public String getCommand() {
		return this.command;
	}

}
